﻿using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Fine.CRM.Common
{
    class CacheItem
    {
        public DateTime ExpiresOn { get; private set; }
        public object value { get; private set; }

        public CacheItem(object value, int timeout)
        {
            this.value = value;
            this.ExpiresOn = DateTime.UtcNow.AddSeconds(timeout);
        }
    }
    
    public sealed class ObjectCache
    {
        const int DEFAULT_CACHE_TIMEOUT = 1800; //30 minutes
        const int MAX_CACHE_TIMEOUT = 43200; //12 hours
        const int MIN_CACHE_TIMEOUT = 5; //5 seconds

        private static volatile ObjectCache instance;
        private static object syncRoot = new object();
        private static volatile Dictionary<string, CacheItem> _cache;

        private ObjectCache()
        {
            _cache = new Dictionary<string, CacheItem>();
        }

        public static ObjectCache Instance
        {
            get
            {
                if (instance == null)
                {
                    lock(syncRoot)
                    {
                        if(instance == null)
                            instance = new ObjectCache();
                    }
                }

                return instance;
            }
        }

        public bool ContainsKey(string key)
        {
            if(_cache.ContainsKey(key))
            {
                if(_cache[key].ExpiresOn > DateTime.UtcNow)
                {
                    return true;
                }
                //cached value is expired so remove it now.
                this.Remove(key);
            }

            return false;
        }
        
        public void Add<T>(string key, T value)
        {
            this.Add<T>(key, value, DEFAULT_CACHE_TIMEOUT);
        }
        
        public void Add<T>(string key, T value, int timeout)
        {
            CacheItem item = new CacheItem(value,timeout);

            if(_cache.ContainsKey(key))
            {
                _cache[key] = item;
            }
            else
            {
                _cache.Add(key,item);
            }
        }

        public T Get<T>(string key)
        {
            if (this.ContainsKey(key))
            {
                object value = _cache[key].value;
                return (T)((object)Convert.ChangeType(value, typeof(T)));
            }

            return default(T);
        }

        public void Remove(string key)
        {
            lock (syncRoot)
            {
                if (_cache.ContainsKey(key))
                {
                    _cache.Remove(key);
                }
            }
        }

    }
}
